// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo

#include "coin2/exchange/huobi_futures/api_order/native_rest_async_asio_subscriber.h"

#include <set>

namespace coin2::exchange::huobi_futures::api_order {

void HuobiFuturesRestAsyncAsioSubscriber::QueryBalance() {
  RestParams params;
  if (is_readonly_order_system_ && symcache_->is_linear()) {
    // query both of cross and non-cross balance for linear-swap
    {
      auto http_context = rest_client()->QueryAccountNonCrossBalance(params);
      http_context->handler =
          std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
      http_driver_->PostAsyncContext(http_context);
    }
    {
      auto http_context = rest_client()->QueryAccountCrossBalance(params);
      http_context->handler =
          std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
      http_driver_->PostAsyncContext(http_context);
    }
    return;
  }
  auto http_context = rest_client()->QueryAccountInfo(params);
  http_context->handler =
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiFuturesRestAsyncAsioSubscriber::QueryPosition() {
  RestParams params;
  if (is_readonly_order_system_ && symcache_->is_linear()) {
    {
      auto http_context = rest_client()->QueryAccountNonCrossPosition(params);
      http_context->handler =
          std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
      http_driver_->PostAsyncContext(http_context);
    }
    {
      auto http_context = rest_client()->QueryAccountCrossPosition(params);
      http_context->handler =
          std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
      http_driver_->PostAsyncContext(http_context);
    }
    return;
  }
  auto http_context = rest_client()->QueryPositionInfo(params);
  http_context->handler =
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiFuturesRestAsyncAsioSubscriber::QueryOpenOrders(const std::string& native_symbol) {
  RestParams params;
  SetParamsSymbol(native_symbol, &params);
  auto http_context = rest_client()->QueryOpenOrders(params);
  http_context->handler =
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiFuturesRestAsyncAsioSubscriber::QueryCancelOrders(const std::string& native_symbol) {
  RestParams params;
  SetParamsSymbol(native_symbol, &params);
  params.set_trade_type(0);    // all trade types
  params.set_type(1);          // all orders(it might be correct with 2, finished only)
  params.set_status("5,6,7");  // 5. Orders cancelled with partially matched;
                               // 6. Orders fully matched; 7. Orders cancelled;
  auto http_context = rest_client()->QueryHistoryOrders(params);
  http_context->handler =
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiFuturesRestAsyncAsioSubscriber::QueryFill(const std::string& native_symbol) {
  RestParams params;
  SetParamsSymbol(native_symbol, &params);  // base native_symbol
  params.set_trade_type(0);
  auto http_context = rest_client()->QueryFills(params);
  http_context->handler =
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiFuturesRestAsyncAsioSubscriber::QueryTransfer(const std::string& native_symbol) {
  RestParams params;
  SetParamsSymbol(native_symbol, &params);
  params.set_symbol(native_symbol);
  auto http_context = rest_client()->QueryTransfer(params);
  http_context->handler =
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiFuturesRestAsyncAsioSubscriber::SetParamsSymbol(
    const std::string& native_symbol,
    RestParams* params) const {
  if (is_swap()) {
    params->set_contract_code(native_symbol);
  } else {
    params->set_symbol(native_symbol);
  }
}

bool HuobiFuturesRestAsyncAsioSubscriber::SkipQuery(
    const std::string& task_name,
    const std::string& native_symbol) {
  if (!is_swap()) {
    return false;
  }
  return RestSubscriberBase::SkipQuery(task_name, native_symbol);
}

void HuobiFuturesRestAsyncAsioSubscriber::SubscribeAsync() {
  AddPeriodicTask(
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::QueryBalance, this),
      config_.query_account_balance_period_sec(),
      "query_balance");

  AddPeriodicTask(
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::QueryPosition, this),
      config_.query_account_position_period_sec(),
      "query_position");

  std::set<std::string> symbol_list;
  for (const auto prod : symcache_->GetProducts()) {
    const auto& product = *prod;
    std::string query_string;
    if (is_swap()) {
      query_string = product.order_native_symbol();
    } else {
      query_string = product.base().native_currency();
    }
    if (symbol_list.find(query_string) == symbol_list.end()) {
      symbol_list.insert(query_string);
    }
  }

  AddPeriodicTask(
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::QueryOpenOrders, this, std::placeholders::_1),
      config_.query_open_orders_period_sec(),
      "query_open_orders",
      symbol_list);

  AddPeriodicTask(
      std::bind(
          &HuobiFuturesRestAsyncAsioSubscriber::QueryCancelOrders,
          this,
          std::placeholders::_1),
      std::bind(
          &HuobiFuturesRestAsyncAsioSubscriber::SkipQuery,
          this,
          "query_history_orders",
          std::placeholders::_1),
      config_.query_cancel_orders_period_sec(),
      "query_history_orders",
      symbol_list);

  AddPeriodicTask(
      std::bind(&HuobiFuturesRestAsyncAsioSubscriber::QueryFill, this, std::placeholders::_1),
      std::bind(
          &HuobiFuturesRestAsyncAsioSubscriber::SkipQuery,
          this,
          "query_fills",
          std::placeholders::_1),
      config_.query_fill_period_sec(),
      "query_fills",
      symbol_list);
}

}  // namespace coin2::exchange::huobi_futures::api_order
